<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="robots" content="noindex, nofollow"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="refresh" content="{{ .RefreshFrequency }}" />
    <title>Sablier</title>
    <link rel="preconnect" href="https://fonts.bunny.net" crossorigin>
    <link rel="dns-prefetch" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
    <style>
        :root{--matrix-glyph-size:15px;--matrix-glyph-font-size:15px;--matrix-glyph-front-color:rgba(255, 255, 255, 0.8);--matrix-glyph-tail-color:#0f0;--matrix-overlay-color:rgba(0, 0, 0, 0.12)}
        body,html{margin:0;padding:0;background-color:#000;height:100vh}
        #matrix{display:block;position:fixed;width:100vw;height:100vh}
        .container{align-items:center;display:flex;justify-content:center;position:absolute;top:0;left:0;width:100vw;height:100vh;z-index:1}
        .container .message{background-color:rgba(0,0,0,.85);border:2px solid var(--matrix-glyph-tail-color);padding:15px 20px;margin:0 20px;font-family:Inconsolata,Helvetica,sans-serif;text-align:center;font-size:0;color:var(--matrix-glyph-tail-color);text-shadow:1px 0 2px var(--matrix-glyph-tail-color),-1px 0 2px var(--matrix-glyph-tail-color);box-shadow:1px 0 5px var(--matrix-glyph-tail-color),-1px 0 2px var(--matrix-glyph-tail-color);max-width:640px}
        .container .message h1{margin:0;font-size:52px}
        .container .message p{margin:.3em 0 0 0;font-size:17px;color:var(--matrix-glyph-front-color)}
        .container .details{margin-top:20px}
        .container .details ul{padding:0}
        .container .details code,.container .details span{font-size:11px}
        .container .details code{padding-left:7px}
        .hidden {display:none}
        span.success {color:var(--matrix-glyph-front-color)}
        span.error {color:#A5524C;text-shadow:1px 0 2px #b44038,-1px 0 2px #b44038}
        @media screen and (max-width:820px){
            :root{--matrix-glyph-size:10px;--matrix-glyph-font-size:10px}
            .container .message h1{font-size:38px}
            .container .message p{font-size:13px}
            .container .details code,.container .details span{font-size:11px}
        }
    </style>
</head>
<body>

<div class="container">
    <ul id="matrix-words" class="hidden">
        <li>{{ .DisplayName }}</li>
        <li>{{ .SessionDuration }}</li>
        <li>{{ .Version }}</li>
    </ul>

    <div class="message">
        <h1>Starting <span>{{ .DisplayName }}...</span></h1>
        <p>Your instance(s) will stop after {{ .SessionDuration }} of inactivity.</p>

        <div class="details">
            <ul>
            {{- range $i, $instance := .InstanceStates }}
                <li>
                    <span><span>{{ $instance.Name }}</span>:</span>
                    {{- if $instance.Error }}
                    <span class="error">{{ $instance.Error }}</span>
                    {{- else }}
                    <span class="success">{{ $instance.Status }} ({{ $instance.CurrentReplicas }}/{{ $instance.DesiredReplicas }})</span>
                    {{- end}}
                </li>
                {{ end -}}
        </div>
    </div>
</div>

<canvas id="matrix"></canvas>

<script>
    'use strict';

    /**
     * @param {HTMLCanvasElement} $canvas
     * @constructor
     */
    const Matrix = function ($canvas) {
        const symbols = 'ラドクリフマラソンわたしワタシんょンョたばこタバコとうきょうトウキョウ '.split('');

        /**
         * @return {string}
         */
        const getRandomSymbol = function () {
            return symbols[Math.floor(Math.random() * symbols.length)];
        }

        const ctx = $canvas.getContext('2d');
        ctx.globalCompositeOperation = 'lighter'; // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

        this.redrawInterval = 90; // fade oud speed
        this.glyphSize = 0;
        this.rowsCapacity = 0;
        this.columnsCapacity = 0;

        /**
         * @return {void}
         */
        this.init = () => {
            $canvas.width = $canvas.clientWidth;
            $canvas.height = $canvas.clientHeight;

            this.glyphSize = parseInt(getComputedStyle($canvas).getPropertyValue('--matrix-glyph-size'), 10);
            this.rowsCapacity = Math.ceil($canvas.clientHeight / this.glyphSize);
            this.columnsCapacity = Math.ceil($canvas.clientWidth / this.glyphSize);
        };

        /**
         * @param {string} symbol
         * @param {number} row
         * @param {number} column
         * @param {string} color
         */
        const drawSymbol = (symbol, row, column, color) => {
            if (row > this.rowsCapacity || column > this.columnsCapacity) {
                return;
            }

            ctx.fillStyle = color;
            ctx.font = getComputedStyle($canvas).getPropertyValue('--matrix-glyph-font-size') + ' monospace';

            if (symbol.length > 1) {
                symbol = symbol.charAt(0); // only one char is allowed
            }

            let xOffset = 0, charCode = symbol.charCodeAt(0);

            if (charCode >= 33 && charCode <= 126) { // is ascii
                xOffset = this.glyphSize / 5;
            }

            ctx.fillText(symbol, (column * this.glyphSize) + xOffset, row * this.glyphSize);
        };

        /**
         * @param {number} column
         * @param {number} speed Lowest = fastest, largest = slowest
         * @param {string?} text
         * @param {number?} offset
         */
        const drawLine = (column, speed, text, offset) => {
            let cursor = 0;

            const tailColor = getComputedStyle($canvas).getPropertyValue('--matrix-glyph-tail-color'),
                frontColor = getComputedStyle($canvas).getPropertyValue('--matrix-glyph-front-color');

            const handler = window.setInterval(() => {
                if (column > this.columnsCapacity) {
                    return window.clearInterval(handler);
                }

                if (cursor <= this.rowsCapacity) {
                    let symbol = getRandomSymbol();

                    if (typeof text === 'string' && typeof offset === 'number') {
                        if (cursor >= offset && text.length >= cursor - offset) {
                            symbol = text.charAt(cursor - offset);
                        }
                    }

                    if (typeof symbol === 'string' && symbol !== ' ') {
                        const prev = cursor;

                        window.setTimeout(() => { // redraw with a green color
                            drawSymbol(symbol, prev, column, tailColor);
                        }, speed / 1.3);

                        drawSymbol(symbol, cursor, column, frontColor); // white color first
                    }

                    cursor++;
                } else {
                    window.clearInterval(handler);
                }
            }, speed);
        };

        /**
         * @return {void}
         */
        this.redraw = () => {
            ctx.fillStyle = getComputedStyle($canvas).getPropertyValue('--matrix-overlay-color');
            ctx.fillRect(0, 0, $canvas.clientWidth, $canvas.clientHeight);
        };

        let redrawIntervalHandler = undefined, dropsIntervalHandler = undefined;

        /**
         * @param {HTMLUListElement?} $linesList
         */
        this.run = ($linesList) => {
            if (redrawIntervalHandler === undefined) {
                redrawIntervalHandler = window.setInterval(this.redraw, this.redrawInterval);
            }

            if (dropsIntervalHandler === undefined) {
                const fn = () => {
                    const randomColumn = Math.floor(Math.random() * (this.columnsCapacity + 1)),
                        minSpeed = 200, maxSpeed = 50,
                        randomSpeed = Math.floor(Math.random() * (maxSpeed - minSpeed + 1)) + minSpeed;

                    const list = [];
                    let line = undefined, offset = undefined;

                    if ($linesList !== undefined) {
                        Array.prototype.forEach.call($linesList.querySelectorAll('li'), $li => {
                            const text = $li.innerText.trim();

                            if (text.length > 0) {
                                list.push(text);
                            }
                        });

                        if (list.length > 0 && Math.random() > 0.4) {
                            line = list[Math.floor(Math.random() * list.length)];
                            offset = Math.floor(Math.random() * line.length);

                            if (offset <= 5) {
                                offset *= 3;
                            }
                        }
                    }

                    drawLine(randomColumn, randomSpeed, line, offset);

                    if (dropsIntervalHandler !== undefined) {
                        window.clearInterval(dropsIntervalHandler);
                        dropsIntervalHandler = undefined;
                    }

                    dropsIntervalHandler = window.setInterval(fn, ((minSpeed + maxSpeed) / 2 * this.rowsCapacity) / this.columnsCapacity / 0.5);
                };

                fn();
            }
        };

        /**
         * @return {void}
         */
        this.stop = () => {
            if (redrawIntervalHandler !== undefined) {
                window.clearInterval(redrawIntervalHandler);
                redrawIntervalHandler = undefined;
            }

            if (dropsIntervalHandler !== undefined) {
                window.clearInterval(dropsIntervalHandler);
                dropsIntervalHandler = undefined;
            }
        };

        if (typeof ResizeObserver === 'function') {
            (new ResizeObserver(this.init)).observe($canvas);
        } else {
            this.init();
        }
    };

    (new Matrix(document.getElementById('matrix'))).run(document.getElementById('matrix-words'));
</script>
</body>
</html>